package com.only4play.web.resources;

import com.only4play.auth.AuthenticatedUser;
import com.only4play.auth.CodeContent;
import com.only4play.common.constants.ErrorCodeEnum;
import com.only4play.common.emums.GrantTypeEnum;
import com.only4play.common.exception.ApplicationException;
import com.only4play.auth.AccessTokenContent;
import com.only4play.domain.model.RpcAccessToken;
import com.only4play.service.UserService;
import com.only4play.session.AccessTokenManager;
import com.only4play.session.CodeManager;
import com.only4play.session.RefreshTokenManager;
import com.only4play.session.TicketGrantingTicketManager;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;

import jakarta.servlet.http.HttpServletResponse;

import java.io.IOException;
import java.net.URLDecoder;
import java.nio.charset.StandardCharsets;
import java.util.Objects;

/**
 * @author liyuncong
 * @version 1.0
 * @file AbstractBaseResource
 * @brief 基础登录控制器
 * @details 基础登录控制器
 * @date 2023-11-18
 * <p>
 * Edit History
 * ----------------------------------------------------------------------------
 * DATE                     NAME               DESCRIPTION
 * 2023-11-18               liyuncong          Created
 */
public class AbstractBaseResource {

    @Autowired
    protected CodeManager codeManager;
    @Autowired
    protected TicketGrantingTicketManager ticketGrantingTicketManager;
    @Autowired
    protected AccessTokenManager accessTokenManager;
    @Autowired
    protected RefreshTokenManager refreshTokenManager;

    @Autowired
    protected UserService userService;


    public String generateCodeAndRedirect(String redirectUri, String tgt) {
        String code = codeManager.generate(tgt, true, redirectUri);
        return "redirect:" + authRedirectUri(redirectUri, code);
    }

    public void generateCodeAndRedirect(String redirectUri, String tgt, HttpServletResponse response)
        throws IOException {
        String code = codeManager.generate(tgt, false, redirectUri);
        if (StringUtils.isBlank(redirectUri)) {
            redirectUri = "http://localhost:8080/logout";
        }
        String redirect = URLDecoder.decode(authRedirectUri(redirectUri, code), StandardCharsets.UTF_8);
        response.sendRedirect(redirect);
    }

    public void removeCodeAndRedirect(String redirectUri, String code, HttpServletResponse response)
        throws IOException {
        CodeContent codeContent = codeManager.getAndRemove(code);
        if (StringUtils.isBlank(redirectUri)) {
            redirectUri = "http://localhost:8080/login";
        }
        String redirect = URLDecoder.decode(redirectUri, StandardCharsets.UTF_8);
        response.sendRedirect(redirect);
    }

    public String authRedirectUri(String redirectUri, String code) {
        StringBuilder builder = new StringBuilder(redirectUri);
        if (redirectUri.contains("?")) {
            builder.append("&");
        } else {
            builder.append("?");
        }
        builder.append("code").append("=").append(code);
        return URLDecoder.decode(builder.toString(), StandardCharsets.UTF_8);
    }


    public void validateParams(
        String grantType, String code, String username, String password
    ) {
        if (GrantTypeEnum.AUTHORIZATION_CODE.getMessage().equals(grantType)) {
            if (StringUtils.isBlank(code)) {
                throw new ApplicationException(ErrorCodeEnum.VALIDATE_ERROR, "code is null");
            }
        } else if (GrantTypeEnum.PASSWORD.getMessage().equals(grantType)) {
            if (StringUtils.isAnyBlank(username, password)) {
                throw new ApplicationException(ErrorCodeEnum.VALIDATE_ERROR, "username or password empty");
            }
        } else {
            throw new ApplicationException(ErrorCodeEnum.PARAM_ILLEGAL, "not support");
        }
    }

    public AccessTokenContent validateAuth(
        String grantType, String code, String loginUsername, String loginPassword, String appId
    ) {
        AccessTokenContent accessTokenContent = null;
        if (GrantTypeEnum.AUTHORIZATION_CODE.getMessage().equals(grantType)) {
            CodeContent codeContent = codeManager.getAndRemove(code);
            if (Objects.isNull(codeContent)) {
                throw new ApplicationException(ErrorCodeEnum.VALIDATE_ERROR, "code error or expired");
            }
            AuthenticatedUser<Void> authenticatedUser = ticketGrantingTicketManager.getAndRefresh(codeContent.getTgt());
            if (Objects.isNull(authenticatedUser)) {
                throw new ApplicationException(ErrorCodeEnum.VALIDATE_ERROR, "session error or expired");
            }
            accessTokenContent = new AccessTokenContent(codeContent, authenticatedUser, appId);
        } else if (GrantTypeEnum.PASSWORD.getMessage().equals(grantType)) {
            AuthenticatedUser<Void> authenticatedUser = userService.login(loginUsername, loginPassword);
            if (Objects.isNull(authenticatedUser)) {
                throw new ApplicationException(ErrorCodeEnum.VALIDATE_ERROR, "user login error");
            }
            String tgt = ticketGrantingTicketManager.generate(authenticatedUser);
            CodeContent codeContent = new CodeContent(tgt, false, null);
            accessTokenContent = new AccessTokenContent(codeContent, authenticatedUser, appId);
        }
        return accessTokenContent;
    }

    public RpcAccessToken generateRpcAccessToken(AccessTokenContent accessTokenContent, String accessToken) {
        String newAccessToken = accessToken;
        if (StringUtils.isBlank(newAccessToken) || !accessTokenManager.refresh(newAccessToken)) {
            newAccessToken = accessTokenManager.generate(accessTokenContent);
        }
        String refreshToken = refreshTokenManager.generate(accessToken, accessTokenContent);
        return new RpcAccessToken(newAccessToken, accessTokenManager.getExpiresIn(), refreshToken,
            accessTokenContent.getAuthenticatedUser());
    }

}
